﻿

#include "WaitEvent.h"

namespace base
{


    WaitableEvent::WaitableEvent(bool manual_reset, bool init_state):
        handle_(NULL)
    {
        /*
        对于手动复位的（manual_reset=TRUE）的Event，当多个线程等待同一个Event时，一次SetEvent()激活所有等待线程。
        对于自动复位的（manual_reset=FALSE）的Event，当多个线程等待同一个Event时，一次SetEvent()激活一个等待线程。
        init_state指定事件对象的初始状态。如果为TRUE，初始状态为有信号状态；否则为无信号状态
        */
#if defined(WIN32)
        handle_ = CreateEvent(NULL, manual_reset, init_state, NULL);
#else
        handle_ = new(std::nothrow) event_t;

        if(handle_ == NULL)
        {
            assert(false);
        }

        handle_->state = init_state;
        handle_->manual_reset = manual_reset;

        if(pthread_mutex_init(&handle_->mutex, NULL))
        {
            delete handle_;
        }

        if(pthread_cond_init(&handle_->cond, NULL))
        {
            pthread_mutex_destroy(&handle_->mutex);
            delete handle_;
        }

#endif
    }

    WaitableEvent::~WaitableEvent()
    {
        if(!handle_)
        {
            assert(false);
        }

#if defined(WIN32)
        CloseHandle(handle_);
#else
        pthread_cond_destroy(&handle_->cond);
        pthread_mutex_destroy(&handle_->mutex);
        delete handle_;
#endif
    }

    int WaitableEvent::Reset()
    {
        //ResetEvent(handle_);
#if defined(WIN32)
        /*
        这个函数把指定的事件对象设置为无信号状态
        函数成功，返回非0值，否则返回0值，可以调用GetLastError得到错误的详细信息。
        */
        //ResetEvent 返回非零表示成功
        if(ResetEvent(handle_) != 0)
        {
            return 0;
        }
        else
        {
            GetLastError();
            return -1;
        }

#else

        if(pthread_mutex_lock(&handle_->mutex) != 0)
        {
            return -1;
        }

        handle_->state = false;

        if(pthread_mutex_unlock(&handle_->mutex) != 0)
        {
            return -1;
        }

        return 0;
#endif
    }

    int WaitableEvent::Signal()
    {
        //SetEvent(handle_);
#if defined(WIN32)
        SetEvent(handle_);
        return 0;
#else

        if(pthread_mutex_lock(&handle_->mutex) != 0)
        {
            return -1;
        }

        handle_->state = true;

        if(handle_->manual_reset)
        {
            if(pthread_cond_broadcast(&handle_->cond))
            {
                return -1;
            }
        }
        else
        {
            if(pthread_cond_signal(&handle_->cond))
            {
                return -1;
            }
        }

        if(pthread_mutex_unlock(&handle_->mutex) != 0)
        {
            return -1;
        }

        return 0;
#endif
    }

    int WaitableEvent::IsSignaled()
    {
        return TimedWait(0);
    }

    int WaitableEvent::Wait()
    {
// DWORD result = WaitForSingleObject(handle_, INFINITE);
#if defined(WIN32)
        DWORD ret = WaitForSingleObject(handle_, INFINITE);

        if(ret != WAIT_OBJECT_0)
        {
            assert(false);
            return -1;
        }

        return 0;
#else

        if(pthread_mutex_lock(&handle_->mutex) != 0)
        {
            return -1;
        }

        while(!handle_->state)
        {
            /*
             成功返回0，出错返回错误编号
            */
            if(pthread_cond_wait(&handle_->cond, &handle_->mutex) != 0)
            {
                pthread_mutex_unlock(&handle_->mutex);
                return -1;
            }
        }

        if(!handle_->manual_reset)
        {
            handle_->state = false;
        }

        if(pthread_mutex_unlock(&handle_->mutex) != 0)
        {
            return -1;
        }

        return 0;
#endif
    }

    int WaitableEvent::TimedWait(unsigned int millsecs)
    {
#if defined(WIN32)
        DWORD ret = WaitForSingleObject(handle_, static_cast<DWORD>(millsecs));

        switch(ret)
        {
            case WAIT_OBJECT_0:
                return 0;

            case WAIT_TIMEOUT:
                return -1;
        }

        return -1;
#else
        int rc = 0;
        struct timespec abstime;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        abstime.tv_sec = tv.tv_sec + millsecs / 1000;
        abstime.tv_nsec = tv.tv_usec * 1000 + (millsecs % 1000) * 1000000;

        if(abstime.tv_nsec >= 1000000000)
        {
            abstime.tv_nsec -= 1000000000;
            abstime.tv_sec++;
        }

        if(pthread_mutex_lock(&handle_->mutex) != 0)
        {
            return -1;
        }

        while(!handle_->state)
        {
            if(rc = pthread_cond_timedwait(&handle_->cond, &handle_->mutex, &abstime))
            {
                if(rc == ETIMEDOUT)
                {
                    break;
                }

                pthread_mutex_unlock(&handle_->mutex);
                return -1;
            }
        }

        if(rc == 0 && !handle_->manual_reset)
        {
            handle_->state = false;
        }

        if(pthread_mutex_unlock(&handle_->mutex) != 0)
        {
            return -1;
        }

        if(rc == ETIMEDOUT)
        {
            //timeout return 1
            return 1;
        }

        //wait event success return 0
        return 0;
#endif
    }
}  // namespace hbase
